package app

import (
	"net/tcpclient"
	cm "share/msg"
	"share/session"
	"time"
	"util/log"
)

type AppClient struct {
	*tcpclient.Tcpconn
	appId      uint64
	lastpong   time.Time
	quit       chan bool
	applist    []cm.AppInfo
	appType    int
	appIp      string
	appPort    int
	centerIp   string
	centerPort int
}

func (ac *AppClient) OnClose() {
	log.TraceInfo("app", "disconnect")
	ac.applist = ac.applist[:0]
}

func (ac *AppClient) OnRecv(data []byte, size int) {
	msg := cm.CenterMsg{}
	if err := cm.DecodeMsg(data[:size], &msg); err != nil {
		panic("recv unknown msg")
		return
	}

	switch msg.MsgId {
	case cm.APP_MSG_REG_BAK:
		ac.regAppBak(data[:size])
		log.LogDebug("app> recv msg APP_MSG_REG_BAK")
	case cm.APP_MSG_PONG:
		ac.pongBak()
	case cm.APP_MSG_LIST_BAK:
		ac.appListBak(data[:size])
		log.LogDebug("app> recv msg APP_MSG_LIST_BAK")
	}
}

func (ac *AppClient) Run(centerip string, centerport int, t int, ip string, port int) {
	ac.appType = t
	ac.appIp = ip
	ac.appPort = port
	ac.centerIp = centerip
	ac.centerPort = centerport
	ac.appId = session.GetAppId(t, port)
	ac.connectCenter()
	go ac.keepAlive()
}

func (ac *AppClient) Shutdown() {
	ac.quit <- true
}

func (ac *AppClient) AppId() uint64 {
	return ac.appId
}

func (ac *AppClient) AppList() []cm.AppInfo {
	return ac.applist
}

func (ac *AppClient) Status(loadPoint int) {
	status := cm.Status{}
	status.MsgId = cm.APP_MSG_STATUS
	status.LoadPoint = loadPoint
	if buf, err := cm.EncodeMsg(status); err == nil {
		ac.Send(buf, len(buf))
	}
}

func (ac *AppClient) connectCenter() {
	if err := ac.Connect(ac.centerIp, ac.centerPort); err != nil {
		log.TraceInfo("appclient", "center server is down")
	} else {
		ac.regApp(ac.appType, ac.appIp, ac.appPort)
	}
}

func (ac *AppClient) keepAlive() {
	tick := time.NewTicker(5 * time.Second)
	defer tick.Stop()
loop:
	for {
		select {
		case <-ac.quit:
			break loop
		case <-tick.C:
			if ac.Connected() {
				ac.ping()
			} else {
				ac.connectCenter()
			}
		}
	}
}

func (ac *AppClient) ping() {
	now := time.Now()
	dur := now.Sub(ac.lastpong)
	if dur.Seconds() >= 15 {
		//超时
		ac.Close()
		return
	}

	ping := cm.Ping{}
	ping.MsgId = cm.APP_MSG_PING
	if buf, err := cm.EncodeMsg(ping); err == nil {
		ac.Send(buf, len(buf))
	}
}

func (ac *AppClient) pongBak() {
	ac.lastpong = time.Now()
}

func (ac *AppClient) appListBak(data []byte) {
	msg := cm.AppListBak{}

	if err := cm.DecodeMsg(data, &msg); err != nil {
		log.LogError("encode msg failed")
		return
	}

	ac.applist = msg.Apps

	log.LogDebug("applist:", ac.applist)
}

func (ac *AppClient) regApp(t int, ip string, port int) {
	reg := cm.RegApp{}
	reg.MsgId = cm.APP_MSG_REG
	reg.App.AppId = ac.appId
	reg.App.AppType = t
	reg.App.AppIp = ip
	reg.App.AppPort = port
	buf, err := cm.EncodeMsg(reg)
	if err != nil {
		panic("pkg error")
	}

	if ac.Send(buf, len(buf)) > 0 {
		log.TraceInfo("app", "send reg app")
	}
}

func (ac *AppClient) regAppBak(data []byte) {

	msg := cm.RegAppBak{}

	if err := cm.DecodeMsg(data, &msg); err != nil {
		log.LogError("encode msg failed")
		return
	}

	if msg.AppId == ac.appId {
		ac.lastpong = time.Now()
		log.TraceInfo("app", "reg app ok, id:", ac.appId)
	} else {
		//可能是名称冲突,换一个id
		ac.AppId() = session.GetAppId(ac.appType, ac.appPort)
		ac.Close()
		log.TraceInfo("app", "reg app failed, now closed")
	}
}

func CreateAppClient() *AppClient {
	ac := &AppClient{}
	ac.Tcpconn = tcpclient.CreateTcpclient()
	ac.SetHandler(ac)
	//ac.Debug(true, "app>")
	ac.quit = make(chan bool)
	return ac
}
